<?php
declare(strict_types=1);

namespace App\Utils\Other\VerifyCode;

use Hyperf\Context\Context;
use Hyperf\Contract\SessionInterface;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Psr\Http\Message\ResponseInterface as Psr7ResponseInterface;
use GdImage;

/**
 * 拖拽验证码
 * @author zx
 * @package App\Utils\Other\VerifyCode
 */
Class VerifyCode {

    /**
     * @var SessionInterface
     */
    #[Inject]
    private SessionInterface $session;

    /**
     * 合成图像
     * @var GdImage|null
     */
    protected ?GdImage $im;

    /**
     * 背景原图
     * @var GdImage|null
     */
    protected ?GdImage $im_fullbg;

    /**
     * 背景图像
     * @var GdImage|null
     */
    protected ?GdImage $im_bg;

    /**
     * 滑动图像
     * @var GdImage|null
     */
    protected ?\GdImage $im_slide;

    /**
     * 背景宽高
     * @var int
     */
    protected int $bg_width = 240;
    protected int $bg_height = 150;

    /**
     * 记号图标宽高
     * @var int
     */
    protected int $mark_width = 50;
    protected int $mark_height = 50;

    /**
     * 背景图片数量
     * @var int
     */
    protected int $bg_num = 6;

    /**
     * 记号XY轴
     * @var int
     */
    protected int $_x = 0;
    protected int $_y = 0;

    /**
     * 容错象素 越大体验越好，越小破解难道越高
     * @var int
     */
    protected int $_fault = 5;

    /**
     * 生成验证码图片
     * @return Psr7ResponseInterface
     */
    public function make($isWebp=true):Psr7ResponseInterface
    {
        // 输出到浏览器
        $response = Context::get(Psr7ResponseInterface::class);
        if (!$isWebp || !function_exists('imagewebp')) {
            $isWebp = false;
        }
        $headers['Content-Type'] = $isWebp?'image/webp':'image/png';
        foreach ($headers as $name => $header) {
            $response = $response->withHeader($name, $header);
            Context::set(Psr7ResponseInterface::class, $response);
        }

        $this->init();
        $this->slide();
        $this->bg();
        $this->merge();
        ob_start();
        $this->imgout($isWebp);
        $content = ob_get_clean();
        $this->destroy();

        return $response->withBody(new SwooleStream($content));
    }

    /**
     * 验证码初始化
     */
    protected function init()
    {
        $bg = mt_rand(1, $this->bg_num);
        $fileBg = BASE_PATH. "/public/static/images/code/bg/{$bg}.png";
        $this->im_fullbg = imagecreatefrompng($fileBg);
        $this->im_bg = imagecreatetruecolor($this->bg_width, $this->bg_height);
        imagecopy($this->im_bg, $this->im_fullbg, 0, 0, 0, 0, $this->bg_width, $this->bg_height);
        $this->im_slide = imagecreatetruecolor($this->mark_width, $this->bg_height);
        $this->_y = mt_rand(0, $this->bg_height-$this->mark_height-1);

        $this->_x = mt_rand(50,$this->bg_width-$this->mark_width-1);
        $this->session->set('tncode_r', $this->_x);
    }

    /**
     * 验证码检测
     * @param string $offset
     * @return bool
     */
    public function check(int $offset){
        if(!$this->session->has('tncode_r')){
            return false;
        }
        $tncode = $this->session->get('tncode_r');
        $ret = abs($tncode-$offset) <= $this->_fault;
        if ($ret) {
            $this->session->forget('tncode_r');
            return $ret;
        }

        $err = $this->session->get('tncode_err', 0);
        if ($err >= 10) {
            $this->session->forget('tncode_r');
            $this->session->forget('tncode_err');
        } else {
            $this->session->set('tncode_err', $err + 1);
        }
        return $ret;
    }

    /**
     * 销毁生成的资源
     */
    protected function destroy(){
        imagedestroy($this->im);
        imagedestroy($this->im_fullbg);
        imagedestroy($this->im_bg);
        imagedestroy($this->im_slide);
    }

    /**
     * 合并图片
     */
    protected function merge(){
        $this->im = imagecreatetruecolor($this->bg_width, $this->bg_height*3);
        imagecopy($this->im, $this->im_bg,0, 0 , 0, 0, $this->bg_width, $this->bg_height);
        imagecopy($this->im, $this->im_slide,0, $this->bg_height , 0, 0, $this->mark_width, $this->bg_height);
        imagecopy($this->im, $this->im_fullbg,0, $this->bg_height*2 , 0, 0, $this->bg_width, $this->bg_height);
        imagecolortransparent($this->im,0);//16777215
    }

    /**
     * 生成图片
     */
    protected function imgout(bool $isWebp=true)
    {
        if($isWebp) {
            $func = 'imagewebp';
            $quality = 40;
        } else {
            $func = 'imagepng';
            $quality = 7;
        }
        $func($this->im, null, $quality);
    }

    /**
     * 生成背景图
     */
    protected function bg()
    {
        $fileMark = BASE_PATH. '/public/static/images/code/img/mark.png';
        $im = imagecreatefrompng($fileMark);
        imagecolortransparent($im, 0);
        imagecopy($this->im_bg, $im, $this->_x, $this->_y, 0, 0, $this->mark_width, $this->mark_height);
        imagedestroy($im);
    }

    /**
     * 生成滑动图片
     */
    protected function slide()
    {
        $fileMark = BASE_PATH.'/public/static/images/code/img/mark2.png';
        $imgMark = imagecreatefrompng($fileMark);
        imagecopy($this->im_slide, $this->im_fullbg, 0, $this->_y, $this->_x, $this->_y, $this->mark_width, $this->mark_height);
        imagecopy($this->im_slide, $imgMark,0, $this->_y , 0, 0, $this->mark_width, $this->mark_height);
        imagecolortransparent($this->im_slide,0);//16777215
        imagedestroy($imgMark);
    }
}